package dev.ece.core.texture;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL30;

import dev.ece.core.clearup.ClearUpGLRubbish;
import dev.ece.core.clearup.ClearUpGLRubbishManager;
import dev.ece.util.tools.ploy.TextureEdgePloy;
import dev.ece.util.tools.ploy.TexturePloy;

public class FBOS implements ClearUpGLRubbish {

	private int width;
	
	private int height;
	
	private int framebuffer;
	
	private int renderbuffer;
	
	private Texture[] textures;
	
	public FBOS(int width, int height, int count) {
		this.width = width;
		this.height = height;
		this.textures = new Texture[count];
	}
	
	public void createBegin() {
		framebuffer = GL30.glGenFramebuffers();
		renderbuffer = GL30.glGenRenderbuffers();
		GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
		GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, renderbuffer);
		GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL14.GL_DEPTH_COMPONENT16, width, height);
	}
	
	public void createTexture(int index, int internalformat, int format, int type, TexturePloy ploy) {
		textures[index] = new Texture(width, height, GL11.glGenTextures(), GL11.GL_TEXTURE_2D);
		textures[index].bind();
		ploy.beforeGlTexImage2D();
		GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, internalformat, width, height, 0, format, type, 0);
		ploy.afterGlTexImage2D();
		GL30.glFramebufferTexture2D(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0 + index, GL11.GL_TEXTURE_2D, textures[index].getTexture(), 0);
	}
	
	public void createFinish() throws Exception {
		int[] attachments = new int[this.textures.length];
		for(int i=0; i<attachments.length; i++) {
			attachments[i] = GL30.GL_COLOR_ATTACHMENT0 + i;
		}
		GL30.glDrawBuffers(attachments);
		GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_DEPTH_ATTACHMENT, GL30.GL_RENDERBUFFER, renderbuffer);
		boolean isComplete = GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER) == GL30.GL_FRAMEBUFFER_COMPLETE;
		GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
		Texture.unbind();
		if(!isComplete) {
			for(Texture texture: this.textures) {
				if(texture != null) {
					texture.destroy();
				}
			}
			throw new Exception("fbo创建失败");
		}
	}
	
	/**
	 * 延迟渲染FBOS
	 * @return
	 * @throws Exception
	 */
	public static FBOS createDeferredFBOS(int width, int height) throws Exception {
		FBOS fbos = new FBOS(width, height, 4);
		fbos.createBegin();
		//位置缓冲
		fbos.createTexture(0, GL30.GL_RGBA16F, GL11.GL_RGBA, GL11.GL_FLOAT, TextureEdgePloy.getInstance());
		//法线颜色缓冲 + shininess
		fbos.createTexture(1, GL30.GL_RGBA16F, GL11.GL_RGBA, GL11.GL_FLOAT, TextureEdgePloy.getInstance());
		//颜色 + 镜面颜色缓冲
		fbos.createTexture(2, GL30.GL_RGBA16F, GL11.GL_RGBA, GL11.GL_FLOAT, TextureEdgePloy.getInstance());
		//其他游戏设置 0、是否可以贴花
		fbos.createTexture(3, GL30.GL_RGBA16F, GL11.GL_RGBA, GL11.GL_FLOAT, TextureEdgePloy.getInstance());
		fbos.createFinish();
		return fbos;
	}
	
	public void begin() {
		GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, framebuffer);
		GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT | GL11.GL_COLOR_BUFFER_BIT);
	}
	
	public Texture[] getTextures() {
		return textures;
	}

	public static void finish() {
		GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);
	}
	
	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public void destroy() {
		for(Texture texture: this.textures) {
			if(texture != null) {
				texture.destroy();
			}
		}
		this.textures = null;
		ClearUpGLRubbishManager.push(this);
	}

	@Override
	public void clearup() {
		GL30.glDeleteFramebuffers(framebuffer);
		GL30.glDeleteRenderbuffers(renderbuffer);
	}
	
}
